package service

import (
	"context"
	"encoding/json"
	"errors"
	"gorm.io/gorm"
	"shop-bean/constant"
	"shop-bean/model"
	"shop-bean/request"
	"shop-common/library/loggerV2"
	"shop-common/library/variables"
	"shop-common/utils"
	"shop-common/utils/pagehelper"
	"shop-service/repository"
	"time"
)

type IProductService interface {
	FindById(id uint) (prod *model.Product, err error)
	FindPage(ctx context.Context, paginator *pagehelper.Paginator) (*pagehelper.Record, error)
	Create(ctx context.Context, request *request.ProductRequest) (uint, error)
	Update(ctx context.Context, request *request.ProductRequest, dbProduct *model.Product) error
	Delete(ctx context.Context, product *model.Product) error
	FindBySkuId(ctx context.Context, skuId uint) (*model.Sku, error)
	FindByTagIdPage(ctx context.Context, paginator *pagehelper.Paginator, tagId uint) (*pagehelper.Record, error)
}

type ProductService struct {
	db                         *gorm.DB
	attachFileService          IAttachFileService
	prodTagReferenceRepository repository.IProdTagReferenceRepository
	repository                 repository.IProductRepository
}

func NewProductService(attachFileService IAttachFileService,
	prodTagReferenceRepository repository.IProdTagReferenceRepository,
	repository repository.IProductRepository) IProductService {
	return &ProductService{
		variables.GormDB(),
		attachFileService,
		prodTagReferenceRepository,
		repository,
	}
}

func (s *ProductService) FindById(id uint) (prod *model.Product, err error) {
	prod, err = s.repository.FindById(s.db, id)
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		loggerV2.Error(err)
		return nil, err
	}
	return prod, nil
}

func (s *ProductService) FindBySkuId(ctx context.Context, skuId uint) (*model.Sku, error) {
	sku, err := s.repository.FindBySkuId(s.db, skuId)
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		loggerV2.Errorf("findBySkuId sku failed: err %v, skuId %d", err, skuId)
		return nil, err
	}
	return sku, nil
}

func (s *ProductService) FindPage(ctx context.Context, paginator *pagehelper.Paginator) (*pagehelper.Record, error) {
	count, err := s.repository.Count(s.db, paginator.Params)
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		loggerV2.Errorf("find product to count failed: err %v, params %+v", err, paginator.Params)
		return paginator.Record, err
	}

	page, err := s.repository.FindPage(s.db, paginator.Params)
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		loggerV2.Errorf("find product to page: err: %v, params %+v", err, paginator.Params)
		return paginator.Record, err
	}

	return paginator.Result(page, count)
}

func (s *ProductService) Create(ctx context.Context, request *request.ProductRequest) (uint, error) {
	product := setProduct(request)
	product.SkuList = setSkuList(request.SkuList)
	return product.ProdId, s.db.Transaction(func(tx *gorm.DB) error {
		if err := s.repository.Create(tx, product); err != nil {
			loggerV2.Error(err)
			return err
		}

		err := s.prodTagReferenceRepository.Creates(tx, setTagList(product.ShopId, product.ProdId, request.TagList))
		if err != nil {
			loggerV2.Error(err)
			return err
		}

		// 商品主图片 + 规格图片 修改为未过期
		fileIds := getOssFileIds(request)
		if len(fileIds) == 0 {
			return nil
		}

		fileIds = append(fileIds, request.ContentFileIds...)

		// 修改图片状态为 未过期
		if err := s.attachFileService.UpdateBatchAttachFileStatus(ctx, tx, fileIds, constant.FILE_NORMAL); err != nil {
			return err
		}

		return nil
	})
}

func (s *ProductService) Update(ctx context.Context, req *request.ProductRequest, dbProduct *model.Product) error {
	nowProduct := setProduct(req)
	var replaceSku []model.Sku
	err := s.db.Transaction(func(tx *gorm.DB) error {
		if err := s.repository.Updates(tx, nowProduct, map[string]interface{}{
			"prod_name":            nowProduct.ProdName,
			"ori_price":            nowProduct.OriPrice,
			"brief":                nowProduct.Brief,
			"content":              nowProduct.Content,
			"imgs":                 nowProduct.Imgs,
			"status":               nowProduct.Status,
			"category_id":          nowProduct.CategoryId,
			"sold_num":             nowProduct.SoldNum,
			"total_stocks":         nowProduct.TotalStocks,
			"delivery_mode":        nowProduct.DeliveryMode,
			"delivery_template_id": nowProduct.DeliveryTemplateId,
			"sku_tags":             nowProduct.SkuTags,
		}); err != nil {
			loggerV2.Error(err)
			return err
		}

		if len(dbProduct.SkuList) > 0 {
			var skuIds []uint
			for _, sku := range dbProduct.SkuList {
				skuIds = append(skuIds, sku.SkuId)
			}
			// 将所有该商品的sku标记为已删除状态
			if err := s.repository.DeleteBySkuId(tx, skuIds); err != nil {
				loggerV2.Errorf("batch deleteBySkuId failed: err %v, skuIds %s", err, skuIds)
				return err
			}
		}

		// 接口传入sku列表
		var nowSkus []*model.Sku

		for _, reqSku := range req.SkuList {
			sku := &model.Sku{
				Properties: reqSku.Properties,
				OriPrice:   reqSku.OriPrice,
				Price:      reqSku.Price,
				Imgs:       reqTransformImgs(reqSku.Imgs),
				Stocks:     reqSku.Stocks,
				ProdName:   reqSku.ProdName,
				SkuName:    reqSku.SkuName,
				Weight:     reqSku.Weight,
				Volume:     reqSku.Volume,
				Status:     reqSku.Status,
			}

			if contains(dbProduct.SkuList, reqSku) {
				// 如果数据库中原有sku就更新，否者就插入
				sku.SkuId = reqSku.SkuId
				if err := s.repository.UpdatesSku(tx, sku, map[string]interface{}{
					"properties": reqSku.Properties,
					"ori_price":  reqSku.OriPrice,
					"price":      reqSku.Price,
					"imgs":       reqTransformImgs(reqSku.Imgs),
					"stocks":     reqSku.Stocks,
					"prod_name":  reqSku.ProdName,
					"sku_name":   reqSku.SkuName,
					"weight":     reqSku.Weight,
					"volume":     reqSku.Volume,
					"status":     reqSku.Status,
					"deleted_at": gorm.DeletedAt{},
				}); err != nil {
					loggerV2.Error(err)
					return err
				}
				replaceSku = append(replaceSku, model.Sku{SkuId: reqSku.SkuId})
			} else {
				nowSkus = append(nowSkus, sku)
			}
		}

		if len(nowSkus) > 0 {
			if err := s.repository.CreateBatchSku(tx, nowSkus); err != nil {
				variables.Logger.Errorf(ctx, "")
				return err
			}

			for _, item := range nowSkus {
				if item.SkuId > 0 {
					replaceSku = append(replaceSku, model.Sku{SkuId: item.SkuId})
				}
			}
		}

		//更新分组信息
		if err := s.prodTagReferenceRepository.DeleteBatch(tx, req.ProdId); err != nil {
			loggerV2.Errorf("batch delete by prodId prodTagReferenceRepository failed: err %v, prodId %d", err, req.ProdId)
			return err
		}
		if err := s.prodTagReferenceRepository.Creates(tx, setTagList(dbProduct.ShopId, req.ProdId, req.TagList)); err != nil {
			loggerV2.Error(err)
			return err
		}

		return nil
	})
	if err != nil {
		return err
	}

	if len(req.ExpireFileIds) > 0 {
		// 过期图片
		_ = s.attachFileService.UpdateBatchAttachFileStatus(ctx, s.db, req.ExpireFileIds, constant.FILE_OVERDUE)
	}

	// 替换新模型
	tx := s.db.Begin()
	if err := s.repository.ReplaceRelation(tx, dbProduct, replaceSku); err != nil {
		loggerV2.Error(err)
		tx.Rollback()
		return err
	}

	return tx.Commit().Error
}

func (s *ProductService) Delete(ctx context.Context, product *model.Product) error {
	err := s.db.Transaction(func(tx *gorm.DB) error {
		if err := s.repository.Delete(tx, product.ProdId); err != nil {
			return err
		}
		if len(product.SkuList) > 0 {
			var ids []uint
			for _, sku := range product.SkuList {
				ids = append(ids, sku.SkuId)
			}
			if err := s.repository.DeleteSkus(tx, ids); err != nil {
				loggerV2.Error(err)
				return err
			}
		}

		//删除商品关联的分组标签
		err := s.prodTagReferenceRepository.DeleteBatch(tx, product.ProdId)
		if err != nil {
			loggerV2.Errorf("batch delete by prodId prodTagReferenceRepository: err %v, prodId", err, product.ProdId)
			return err
		}

		var fileImgs []request.ImgRequest
		_ = json.Unmarshal([]byte(product.Imgs), &fileImgs)
		fileIds := imgFileIdList(fileImgs)
		// 提取富文本里的图片Id集合
		fileIds = append(fileIds, utils.FindAllEditorFileIds(product.Content)...)
		if len(product.SkuList) > 0 {
			for _, sku := range product.SkuList {
				fileImgs = []request.ImgRequest{}
				_ = json.Unmarshal([]byte(product.Imgs), &sku)
				fileIds = append(fileIds, imgFileIdList(fileImgs)...)
			}
		}

		// 修改图片状态为 未过期
		return s.attachFileService.UpdateBatchAttachFileStatus(ctx, tx, fileIds, constant.FILE_OVERDUE)
	})

	if err != nil {
		return err
	}

	tx := s.db.Begin()
	if err := s.repository.DeleteRelation(tx, product); err != nil {
		tx.Rollback()
		loggerV2.Error(err)
		return err
	}

	defer func() {

	}()
	return tx.Commit().Error
}

func contains(dbSkuList []model.Sku, sku request.SkuRequest) (exist bool) {
	for _, dbSku := range dbSkuList {
		if dbSku.SkuId == sku.SkuId {
			exist = true
			break
		}
	}

	return exist
}

func setProduct(request *request.ProductRequest) *model.Product {
	return &model.Product{
		ProdId:             request.ProdId,
		ProdName:           request.ProdName,
		ShopId:             constant.SHOP_ID,
		OriPrice:           request.OriPrice,
		Price:              request.Price,
		Brief:              request.Brief,
		Content:            request.Content,
		Imgs:               reqTransformImgs(request.Imgs),
		Status:             request.Status,
		CategoryId:         request.CategoryId,
		TotalStocks:        request.TotalStocks,
		DeliveryMode:       request.DeliveryMode,
		DeliveryTemplateId: request.DeliveryTemplateId,
		SkuTags:            request.SkuTags,
		PutawayAt:          time.Now(),
		//TagList:            req.TagList,
	}
}

func setSkuList(skus []request.SkuRequest) (skuList []model.Sku) {
	for _, sku := range skus {
		skuList = append(skuList, model.Sku{
			Properties:   sku.Properties,
			OriPrice:     sku.OriPrice,
			Price:        sku.Price,
			Imgs:         reqTransformImgs(sku.Imgs),
			ActualStocks: sku.ActualStocks,
			ProdName:     sku.ProdName,
			SkuName:      sku.SkuName,
			Stocks:       sku.Stocks,
			Weight:       sku.Weight,
			Volume:       sku.Volume,
			Status:       sku.Status,
		})
	}

	return skuList
}

func setTagList(shopId uint, prodId uint, tagIds []uint) []*model.ProdTagReference {
	var tagList []*model.ProdTagReference
	for _, tagId := range tagIds {
		tagList = append(tagList, &model.ProdTagReference{
			ShopId: shopId,
			TagId:  tagId,
			ProdId: prodId,
			Status: constant.GLOBAL_ENABLE,
		})
	}
	return tagList
}

func reqTransformImgs(imgs []request.ImgRequest) string {
	if len(imgs) > 0 {
		marshal, err := json.Marshal(imgs)
		if err == nil {
			return string(marshal)
		}
	}
	return ""
}

func getOssFileIds(req *request.ProductRequest) (fileIds []uint) {
	fileIds = imgFileIdList(req.Imgs)
	if len(req.SkuList) > 0 {
		for _, sku := range req.SkuList {
			if len(imgFileIdList(sku.Imgs)) > 0 {
				fileIds = append(fileIds, imgFileIdList(sku.Imgs)...)
			}
		}
	}

	return fileIds
}

// 图片fileIds
func imgFileIdList(imgs []request.ImgRequest) (fileIds []uint) {
	if len(imgs) > 0 {
		for _, img := range imgs {
			if img.Pic != "" && img.FileId > 0 {
				fileIds = append(fileIds, img.FileId)
			}

		}
	}
	return fileIds
}

func (s *ProductService) FindByTagIdPage(ctx context.Context, paginator *pagehelper.Paginator, tagId uint) (*pagehelper.Record, error) {
	count, err := s.repository.CountByTagId(s.db, paginator.Params, tagId)
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		variables.Logger.Errorf(ctx, "find product to count failed: err %v, params %+v", err, paginator.Params)
		return nil, err
	}

	page, err := s.repository.FindByTagIdPage(s.db, paginator.Params, tagId)
	if err != nil {
		variables.Logger.Errorf(ctx, "find product to page: err: %v, params %+v", err, paginator.Params)
		return nil, err
	}

	return paginator.Result(page, count)
}
